package com.zkthink.ceres.authority.controller.auth;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zkthink.base.R;
import com.zkthink.base.controller.SuperCacheController;
import com.zkthink.ceres.authority.dto.auth.RoleAuthoritySaveDTO;
import com.zkthink.ceres.authority.dto.auth.SyncMenuDTO;
import com.zkthink.ceres.authority.entity.auth.*;
import com.zkthink.ceres.authority.enumeration.auth.AuthorizeType;
import com.zkthink.ceres.authority.service.auth.*;
import com.zkthink.ceres.common.constant.BizConstant;
import com.zkthink.ceres.common.constant.CacheKey;
import com.zkthink.ceres.common.service.SerialService;
import com.zkthink.ceres.tenant.entity.Tenant;
import com.zkthink.ceres.tenant.enumeration.TenantStatusEnum;
import com.zkthink.ceres.tenant.service.TenantService;
import com.zkthink.context.BaseContextHandler;
import com.zkthink.database.mybatis.conditions.Wraps;
import com.zkthink.database.properties.DatabaseProperties;
import com.zkthink.dozer.DozerUtils;
import com.zkthink.exception.code.ExceptionCode;
import com.zkthink.log.annotation.SysLog;
import com.zkthink.security.annotation.PreAuth;
import com.zkthink.security.constant.RoleConstant;
import com.zkthink.utils.BeanPlusUtil;
import com.zkthink.utils.TreeUtil;
import com.zkthink.ceres.authority.dto.auth.MenuSaveDTO;
import com.zkthink.ceres.authority.dto.auth.MenuUpdateDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import java.util.*;
import java.util.stream.Collectors;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;


/**
 * <p>
 * 前端控制器 菜单
 * </p>
 *
 * @author ceres
 * @date 2019-07-22
 */
@Slf4j
@Validated
@RestController
@RequestMapping("/menu")
@Api(value = "Menu", tags = "菜单")
@PreAuth(replace = "menu:")
public class MenuController extends
    SuperCacheController<MenuService, Long, Menu, Menu, MenuSaveDTO, MenuUpdateDTO> {

  @Autowired
  private DozerUtils dozer;

  @Autowired
  private TenantService tenantService;

  @Autowired
  private SerialService serialService;

  @Autowired
  private DatabaseProperties databaseProperties;

  @Autowired
  private RoleService roleService;

  @Autowired
  private RoleAuthorityService roleAuthorityService;

  @Autowired
  private ResourceService resourceService;

  @Autowired
  private UserService userService;

  @Override
  public R<Menu> handlerSave(MenuSaveDTO menuSaveDTO) {
    Menu menu = BeanPlusUtil.toBean(menuSaveDTO, Menu.class);
    baseService.saveWithCache(menu);
    return success(menu);
  }

  @Override
  public R<Menu> handlerUpdate(MenuUpdateDTO model) {
    Menu menu = BeanPlusUtil.toBean(model, Menu.class);
    baseService.updateWithCache(menu);
    return success(menu);
  }

  @Override
  public R<Boolean> handlerDelete(List<Long> ids) {
    baseService.removeByIdWithCache(ids);
    return success();
  }

//    /**
//     * 查询用户可用的所有资源
//     *
//     * @param group  分组 <br>
//     * @param userId 指定用户id
//     * @return
//     */
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "group", value = "菜单组", dataType = "string", paramType = "query"),
//            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "long", paramType = "query"),
//    })
//    @ApiOperation(value = "查询用户可用的所有菜单", notes = "查询用户可用的所有菜单")
//    @GetMapping("/menus")
//    public R<List<Menu>> myMenus(@RequestParam(value = "group", required = false) String group,
//                                 @RequestParam(value = "userId", required = false) Long userId) {
//        if (userId == null || userId <= 0) {
//            userId = getUserId();
//        }
//        List<Menu> list = baseService.findVisibleMenu(group, userId);
//        List<Menu> tree = TreeUtil.buildTree(list);
//        return success(tree);
//    }
//
//    @ApiImplicitParams({
//            @ApiImplicitParam(name = "group", value = "菜单组", dataType = "string", paramType = "query"),
//            @ApiImplicitParam(name = "userId", value = "用户id", dataType = "long", paramType = "query"),
//    })
//    @ApiOperation(value = "查询用户可用的所有菜单路由树", notes = "查询用户可用的所有菜单路由树")
//    @GetMapping("/router")
//    public R<List<VueRouter>> myRouter(@RequestParam(value = "group", required = false) String group,
//                                       @RequestParam(value = "userId", required = false) Long userId) {
//        if (userId == null || userId <= 0) {
//            userId = getUserId();
//        }
//        List<Menu> list = baseService.findVisibleMenu(group, userId);
//        List<VueRouter> treeList = dozer.mapList(list, VueRouter.class);
//        return success(TreeUtil.buildTree(treeList));
//    }
//
//    @ApiOperation(value = "查询超管菜单路由树", notes = "查询超管菜单路由树")
//    @GetMapping("/admin/router")
//    public R<List<VueRouter>> adminRouter() {
//        return success(buildSuperAdminRouter());
//    }
//
//    private List<VueRouter> buildSuperAdminRouter() {
//        List<VueRouter> tree = new ArrayList<>();
//        List<VueRouter> children = new ArrayList<>();
//
//        VueRouter tenant = new VueRouter();
//        tenant.setPath("/defaults/tenant");
//        tenant.setComponent("ceres/defaults/tenant/Index");
//        tenant.setHidden(false);
//        // 没有name ，刷新页面后，切换菜单会报错：
//        // [Vue warn]: Error in nextTick: "TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))"
//        // found in
//        // <TagsView> at src/layout/components/TagsView/index.vue
//        tenant.setName("租户管理");
//        tenant.setAlwaysShow(true);
//        tenant.setMeta(RouterMeta.builder()
//                .title("租户管理").breadcrumb(true).icon("")
//                .build());
//        tenant.setId(-2L);
//        tenant.setParentId(-1L);
//        children.add(tenant);
//
//        VueRouter globalUser = new VueRouter();
//        globalUser.setPath("/defaults/globaluser");
//        globalUser.setComponent("ceres/defaults/globaluser/Index");
//        globalUser.setName("全局用户");
//        globalUser.setHidden(false);
//        globalUser.setMeta(RouterMeta.builder()
//                .title("全局用户").breadcrumb(true).icon("")
//                .build());
//        globalUser.setId(-3L);
//        globalUser.setParentId(-1L);
//        children.add(globalUser);
//
//        VueRouter defaults = new VueRouter();
//        defaults.setPath("/defaults");
//        defaults.setComponent("Layout");
//        defaults.setHidden(false);
//        defaults.setName("系统设置");
//        defaults.setAlwaysShow(true);
//        defaults.setMeta(RouterMeta.builder()
//                .title("系统设置").icon("el-icon-coin").breadcrumb(true)
//                .build());
//        defaults.setId(-1L);
//        defaults.setChildren(children);
//
//        tree.add(defaults);
//        return tree;
//    }

  /**
   * 查询系统中所有的的菜单树结构， 不用缓存，因为该接口很少会使用，就算使用，也会管理员维护菜单时使用
   */
  @ApiOperation(value = "查询系统所有的菜单", notes = "查询系统所有的菜单")
  @GetMapping("/tree")
  @SysLog("查询系统所有的菜单")
  public R<List<Menu>> allTree() {
    List<Menu> list = baseService.list(Wraps.<Menu>lbQ().orderByAsc(Menu::getSortValue));
    return success(TreeUtil.buildTree(list));
  }

  /**
   * 查询指定商家的菜单 和 平台菜单对比
   */
  @ApiOperation(value = "查询指定商家的菜单", notes = "查询指定商家的菜单")
  @GetMapping("/getSpecifiedTenantMenu/{account}")
  public R<List<Menu>> getSpecifiedTenantMenu(@PathVariable("account") String account) {

    // 查找平台的菜单
    BaseContextHandler.setTenant(BizConstant.MENU_TENANT);
    List<Menu> list = baseService.list(Wraps.<Menu>lbQ().orderByAsc(Menu::getSortValue));

    // 查询指定商家的菜单
    User user = userService.getByAccount(account);

    List<Menu> tenantMenuList = new ArrayList<>();

    if(user!=null){
      BaseContextHandler.setTenant(userService.getTenantCodeByUserId(user.getId()));
      tenantMenuList = baseService.list(Wraps.<Menu>lbQ().orderByAsc(Menu::getSortValue));
    }

    Map<String,Integer> menuMap = new HashMap<>();
    for(Menu menu:tenantMenuList){
      menuMap.put(menu.getPath(),1);
    }

    // 过滤菜单
    List<Menu> transferredMenuList = TreeUtil.buildTree(list);
    setIsSync(transferredMenuList,menuMap);

    return success(transferredMenuList);
  }

  /**
   * 查询所有商家的菜单和平台比对的结果
   */
  @ApiOperation(value = "查询所有商家的菜单", notes = "查询所有商家的菜单和平台比对的结果")
  @GetMapping("/getAllTenantMenu")
  public R<List<Menu>> getAllTenantMenu() {

    // 查找平台的菜单
    BaseContextHandler.setTenant(BizConstant.MENU_TENANT);
    List<Menu> list = baseService.list(Wraps.<Menu>lbQ().orderByAsc(Menu::getSortValue));

    // 绕过tenantCode拦截
    BaseContextHandler.setTenant("");

    // 查询所有商家的编号
    List<Tenant> tenantList = tenantService.list(Wraps.<Tenant>lbQ().eq(Tenant::getStatus, TenantStatusEnum.NORMAL).eq(Tenant::getReadonly,false));

    // 初始化计数器
    List<Integer> countList = new ArrayList<>(list.size());
    for(Menu menu:list){
      countList.add(0);
    }

    // 查询各个商家的菜单，并设置计数器
    for(Tenant tenant:tenantList){
      Map<String,Integer> menuMap = new HashMap<>();
      String code = tenant.getCode();
      QueryWrapper<Menu> menuQueryWrapper = new QueryWrapper<>();
      menuQueryWrapper.eq("tenant_code",code);
      List<Menu> menuList = baseService.list(menuQueryWrapper);
      for(Menu menu:menuList){
        menuMap.put(menu.getPath(),1);
      }
      for(int i=0;i<list.size();i++){
        if(menuMap.get(list.get(i).getPath())!=null){
          countList.set(i,countList.get(i)+1);
        }
      }
    }

    // 设置总的标志map
    Map<String,Integer> menuMap = new HashMap<>();
    for(int i=0;i<countList.size();i++){
      String path = list.get(i).getPath();
      if(countList.get(i)==tenantList.size()){
        menuMap.put(path,1);
      }
    }

    // 设置同步标志位
    List<Menu> transferredMenuList = TreeUtil.buildTree(list);
    setIsSync(transferredMenuList,menuMap);

    return success(transferredMenuList);
  }

  @ApiOperation("同步菜单")
  @PostMapping("/syncMenu")
  public R<Boolean> syncMenu(@RequestBody SyncMenuDTO syncMenuDTO){
    if(syncMenuDTO == null){
      return R.fail(ExceptionCode.ILLEGALA_ARGUMENT_EX);
    }

    if(StringUtils.isNotBlank(syncMenuDTO.getAccount())){
      String tenantCode = userService.getTenantCodeByAccount(syncMenuDTO.getAccount());
      syncMenuDTO.setTenantCode(tenantCode);
    }


    List<Menu> updatedMenuList = syncMenuDTO.getMenuList();

    //不为空 同步单个商家，为空则同步所有商家
    if(StringUtils.isNotBlank(syncMenuDTO.getTenantCode())){
      R<List<Menu>> originMenuList = getSpecifiedTenantMenu(syncMenuDTO.getAccount());
      compareMenuList(originMenuList.getData(),updatedMenuList,syncMenuDTO);

      syncMenuInner(syncMenuDTO);
    }else{
      R<List<Menu>> originMenuList = getAllTenantMenu();
      compareMenuList(originMenuList.getData(),updatedMenuList,syncMenuDTO);

      List<Tenant> tenantList = tenantService.list(Wraps.<Tenant>lbQ().eq(Tenant::getStatus, TenantStatusEnum.NORMAL).eq(Tenant::getReadonly,false));
      for(Tenant tenant:tenantList){
        //不能修改平台的菜单，也不能修改 用来对比的原始菜单
        if(!BizConstant.SUPER_TENANT.equals(tenant.getCode()) && !BizConstant.MENU_TENANT.equals(tenant.getCode())){
          syncMenuDTO.setTenantCode(tenant.getCode());
          syncMenuInner(syncMenuDTO);
        }
      }
    }
    baseService.clearMenu();
    return R.success();
  }

  private void compareMenuList(List<Menu> originMenuList, List<Menu> updatedMenuList, SyncMenuDTO syncMenuDTO) {
    Map<String,Boolean> selectedMap = new HashMap<>();

    List<Menu> addMenuList = new ArrayList<>();

    List<Menu> delMenuList = new ArrayList<>();

    //判断原始菜单的选择情况
    for(Menu menu:originMenuList){
      if(menu.getIsSync()==1){
        selectedMap.put(menu.getPath(),true);
      }
      if(menu.getChildren()!=null){
        for(Menu childMenu:menu.getChildren()){
          if(childMenu.getIsSync()==1){
            selectedMap.put(childMenu.getPath(),true);
          }
        }
      }
    }

    for(Menu menu:updatedMenuList){
      Boolean selected = selectedMap.get(menu.getPath());
      if(selected==null){
        selected = false;
      }
      //原来是选中的，现在去除了
      if(selected && menu.getIsSync()==0){
        //delMenuList.add(menu);
        List<Menu> tmpMenuList = new ArrayList<>();
        if(menu.getChildren()!=null && menu.getChildren().size()>0){
          for(Menu childMenu:menu.getChildren()){
            Boolean childSelected = selectedMap.get(childMenu.getPath());
            if(childSelected==null){
              childSelected = false;
            }
            if(childSelected && childMenu.getIsSync()==0){
              tmpMenuList.add(childMenu);
            }
          }
          //如果是子集全部删掉，父级也要跟着删
          if(tmpMenuList.size()!=0 && tmpMenuList.size()==menu.getChildren().size()){
            delMenuList.add(menu);
          }
          delMenuList.addAll(tmpMenuList);
        }else{
          delMenuList.add(menu);
        }
      }

      //原来和现在都选中1级菜单，但是可能部分二级被去除了
      if(selected && menu.getIsSync()==1){
        if(menu.getChildren()!=null){
          for(Menu childMenu:menu.getChildren()){
            Boolean childSelected = selectedMap.get(childMenu.getPath());
            if(childSelected==null){
              childSelected = false;
            }
            if(childSelected && childMenu.getIsSync()==0){
              delMenuList.add(childMenu);
            }
          }
        }
      }

      //原来没选中，但是现在选中了
      if(!selected && menu.getIsSync()==1){
        Menu newAddMenu = new Menu();
        BeanUtils.copyProperties(menu,newAddMenu);
        newAddMenu.setChildren(new ArrayList<>());
        addMenuList.add(newAddMenu);
        if(menu.getChildren()!=null){
          for(Menu childMenu:menu.getChildren()){
            Boolean childSelected = selectedMap.get(childMenu.getPath());
            if(childSelected==null){
              childSelected = false;
            }
            if(!childSelected && childMenu.getIsSync()==1){
              newAddMenu.getChildren().add(childMenu);
            }
          }
        }
      }

      //原来和现在都选中1级，并且现在有2级被选中
      if(selected && menu.getIsSync()==1){
        List<Menu> tmpAddNenuList = new ArrayList<>();
        if(menu.getChildren()!=null){
          for(Menu childMenu:menu.getChildren()){
            Boolean childSelected = selectedMap.get(childMenu.getPath());
            if(childSelected==null){
              childSelected = false;
            }
            if(!childSelected && childMenu.getIsSync()==1){
              tmpAddNenuList.add(childMenu);
            }
          }
        }
        if(tmpAddNenuList.size()>0){
          Menu newAddMenu = new Menu();
          BeanUtils.copyProperties(menu,newAddMenu);
          newAddMenu.setChildren(tmpAddNenuList);
          addMenuList.add(newAddMenu);
        }
      }

      //由于前端处理问题，这里也可能是 当前1级也选中，而子级选中部分
      if(menu.getIsSync()==0){
        if(menu.getChildren()!=null && menu.getChildren().size()>0){
          List<Menu> tmpMenuList = new ArrayList<>();
          for(Menu childMenu:menu.getChildren()){
            Boolean childSelected = selectedMap.get(childMenu.getPath());
            if(childSelected==null){
              childSelected = false;
            }
            if(!childSelected && childMenu.getIsSync()==1){
              tmpMenuList.add(childMenu);
            }
          }
          if(tmpMenuList.size()>0){
            Menu newAddMenu = new Menu();
            BeanUtils.copyProperties(menu,newAddMenu);
            newAddMenu.setChildren(tmpMenuList);
            addMenuList.add(newAddMenu);
          }
        }
      }
    }

    syncMenuDTO.setAddMenuList(addMenuList);
    syncMenuDTO.setDelMenuList(delMenuList);

  }

  private void syncMenuInner(SyncMenuDTO syncMenuDTO){
    String tenantCode = syncMenuDTO.getTenantCode();
    BaseContextHandler.setTenant(tenantCode);
    if(!CollectionUtils.isEmpty(syncMenuDTO.getAddMenuList())){
      addMenu(0L, syncMenuDTO.getAddMenuList());
    }
    if(!CollectionUtils.isEmpty(syncMenuDTO.getDelMenuList())){
      delMenu(syncMenuDTO.getDelMenuList());
    }
  }

  /**
   * 增加菜单
   * @param parentId
   * @param addMenuList
   */
  private void addMenu(Long parentId, List<Menu> addMenuList){
    if(CollectionUtils.isEmpty(addMenuList)){
      return;
    }
    DatabaseProperties.Id id = databaseProperties.getId();
    Snowflake snowflake = IdUtil.getSnowflake(id.getWorkerId(), id.getDataCenterId());
    for(Menu menu:addMenuList){
      List<Menu> menuList = baseService.list(Wraps.<Menu>lbQ().eq(Menu::getPath,menu.getPath()));
      Long menuId = 0L;
      if(menuList.size()==0){

          String bakTenantCode = BaseContextHandler.getTenant();
          BaseContextHandler.setTenant(BizConstant.MENU_TENANT);
          List<Resource> resourceList = resourceService.list(Wraps.<Resource>lbQ().eq(Resource::getMenuId,menu.getId()));
          for(Resource resource:resourceList){
            resource.setId(snowflake.nextId());
          }
          BaseContextHandler.setTenant(bakTenantCode);

          menuId = snowflake.nextId();
          menu.setId(menuId);
          menu.setParentId(parentId);
          baseService.saveWithCache(menu);

          resourceService.saveOrUpdateAndRefreshCache(resourceList,menu);

          Role role = roleService.getOne(Wraps.<Role>lbQ().eq(Role::getCode,BizConstant.INIT_ROLE_CODE));

          //判断菜单是不是公共菜单，如果不是，要处理授权
          if(!menu.getIsPublic()){
            if(role != null && role.getStatus()) {
              RoleAuthority roleAuthority = new RoleAuthority();
              roleAuthority.setId(snowflake.nextId());
              roleAuthority.setAuthorityType(AuthorizeType.MENU);
              roleAuthority.setAuthorityId(menuId);
              roleAuthority.setRoleId(role.getId());
              roleAuthorityService.saveOrUpdate(roleAuthority);
            }
          }

          if(role!=null && role.getStatus()){
            for(Resource resource:resourceList){
              RoleAuthority roleAuthority = new RoleAuthority();
              roleAuthority.setId(snowflake.nextId());
              roleAuthority.setAuthorityType(AuthorizeType.RESOURCE);
              roleAuthority.setAuthorityId(resource.getId());
              roleAuthority.setRoleId(role.getId());
              roleAuthorityService.saveOrUpdate(roleAuthority);
            }
          }

          /*List<Long> menuIdList = new ArrayList<Long>(Arrays.asList(menu.getId()));
          List<Long> resourceIdList = resourceList.stream().map(n -> n.getId()).collect(Collectors.toList());
          roleAuthorityService.saveRoleAuthority(RoleAuthoritySaveDTO.builder().menuIdList(menuIdList).resourceIdList(resourceIdList).roleId(role.getId()).build());*/

          if(role!=null){
            roleAuthorityService.cleanUserResource(role.getId());
          }
      }else{
        menuId = menuList.get(0).getId();
      }
      addMenu(menuId,menu.getChildren());
    }
  }

  /**
   * 删除菜单
   * @param delMenuList
   */
  private void delMenu(List<Menu> delMenuList){
    if(CollectionUtils.isEmpty(delMenuList)){
      return;
    }
    for(Menu menu:delMenuList){
      //这里查list,是因为path没有在数据库层面去重
      List<Menu> menuList = baseService.list(Wraps.<Menu>lbQ().eq(Menu::getPath,menu.getPath()));
      if(menuList.size()>0){
        baseService.removeByIdWithCache(menuList.stream().map(Menu::getId).collect(Collectors.toList()));
      }
    }
  }

  /**
   * 递归设置菜单是否同步
   * @param menuList
   * @param menuMap
   */
  private void setIsSync(List<Menu> menuList,Map<String,Integer> menuMap){
    if(menuList==null || menuList.size()==0){
      return;
    }
    for(Menu menu:menuList){
      if(menuMap.get(menu.getPath())!=null){
        menu.setIsSync(1);
      }
      setIsSync(menu.getChildren(),menuMap);
    }
  }

}
